#include <string.h>
#include <unistd.h>
#include <math.h>
#include <grass/gis.h>
#include <grass/raster.h>
#include <grass/display.h>
#include <grass/colors.h>
#include <grass/Vect.h>
#include <grass/form.h>
#include <grass/dbmi.h>
#include "what.h"
#include <grass/glocale.h>

static int nlines = 50;

#define WDTH 5

int what(int once, int txt, int terse, int flash, int width, int mwidth, int topo, int edit )
{
    int type, edit_mode;
    int row, col;
    int nrows, ncols;
    struct Cell_head window;
    int screen_x, screen_y;
    double east, north;
    int button;
    char east_buf[40], north_buf[40];
    double sq_meters;
    double x1, y1, x2, y2, z = 0, l = 0;
    int notty;
    double maxdist;
    int getz = 0;
    struct field_info *Fi;
    int flash_basecolr, flash_colr;

    plus_t line, area = 0, centroid;
    int i;
    struct line_pnts *Points;
    struct line_cats *Cats;
    char buf[1000], *str, title[500];
    dbString html; 
    char *form;
    char *panell;

    if ( terse ) txt = 1; /* force text for terse */
    
    G_get_set_window(&window);

    if(flash)
	G_setup_plot(D_get_d_north(), D_get_d_south(), D_get_d_west(),
		 D_get_d_east(), D_move_abs, D_cont_abs);


    G_begin_polygon_area_calculations();
    nrows = window.rows;
    ncols = window.cols;

    screen_x = ((int) D_get_d_west() + (int) D_get_d_east()) / 2;
    screen_y = ((int) D_get_d_north() + (int) D_get_d_south()) / 2;

    Points = Vect_new_line_struct();
    Cats = Vect_new_cats_struct();
    db_init_string (&html);

    if (!isatty(fileno(stdout))) notty = 1; /* no terminal */
    else notty = 0;

    if(flash){
	panell = G_tempfile();
	flash_basecolr=YELLOW;
    }

    do {
        if(flash)
	   R_panel_save(panell, R_screen_top(), R_screen_bot(),
		        R_screen_left(), R_screen_rite());

	if (!terse) show_buttons(once, flash);
	R_get_location_with_pointer(&screen_x, &screen_y, &button);
	if (!once) {
	    if (button == 3) {
		if(flash)
		   R_panel_delete(panell);
		break;
	    }
	    if (button == 2) {
		if(flash){
		   R_panel_delete(panell);
		   flash_basecolr++;
		   if (flash_basecolr >= G_num_standard_colors())
		       flash_basecolr = 1;
		}
		continue;
	    }
	}

	east = D_d_to_u_col((double) screen_x);
	north = D_d_to_u_row((double) screen_y);

	row = (window.north - north) / window.ns_res;
	col = (east - window.west) / window.ew_res;
	if (row < 0 || row >= nrows) continue;
	if (col < 0 || col >= ncols) continue;

	x1 = D_d_to_u_col((double) (screen_x - WDTH));
	y1 = D_d_to_u_row((double) (screen_y - WDTH));
	x2 = D_d_to_u_col((double) (screen_x + WDTH));
	y2 = D_d_to_u_row((double) (screen_y + WDTH));

	x1 = fabs(x2 - x1);
	y1 = fabs(y2 - y1);

	if (x1 > y1) maxdist = x1;
	else maxdist = y1;
	G_debug(1, "Maximum distance in map units = %f\n", maxdist);

	if(flash)
	   flash_colr = flash_basecolr;
	F_clear ();
	for (i = 0; i < nvects; i++) {
	    Vect_reset_cats ( Cats );
	    /* Try to find point first and only if no one was found try lines,
	       *  otherwise point on line could not be selected ans similarly for areas */
	    line = Vect_find_line(&Map[i], east, north, 0.0, GV_POINT | GV_CENTROID, maxdist, 0, 0);
	    if (line == 0) line = Vect_find_line(&Map[i], east, north, 0.0,
			                         GV_LINE | GV_BOUNDARY | GV_FACE, maxdist, 0, 0);

	    area = 0; 
	    if ( line == 0 ) {
	        area = Vect_find_area(&Map[i], east, north);
	        getz = Vect_tin_get_z(&Map[i], east, north, &z, NULL, NULL);
	    } 

	    G_debug (2, "line = %d, area = %d", line, area);

	    if ( !i && txt ) {
		G_format_easting(east, east_buf, G_projection());
		G_format_northing(north, north_buf, G_projection());
		fprintf(stdout, "\n%s(E) %s(N)\n", east_buf, north_buf);
		if (notty) fprintf(stderr, "\n%s(E) %s(N)\n", east_buf, north_buf);
		nlines++;
	    }

	    strcpy(buf, vect[i]);
	    if ((str = strchr(buf, '@'))) *str = 0;

	    if ( txt ) {
	        fprintf(stdout, "\n%*s in %-*s  ", width, Map[i].name, mwidth, Map[i].mapset); 
	        if (notty) fprintf(stderr, "\n%*s in %-*s  ", width, Map[i].name, mwidth, Map[i].mapset);
	        nlines++;
	    }

	    if (line + area == 0) {
                if ( txt ) {
		    fprintf(stdout, _("Nothing Found.\n"));
		    if (notty) fprintf(stderr, _("Nothing Found.\n"));
		    nlines++;
		}
		continue;
	    } else {
		/* Start form */
		db_set_string (&html, ""); 
		if ( !txt ) {
		    sprintf (title, "%s", Map[i].name );
		    db_append_string (&html, "<HTML><HEAD><TITLE>Form</TITLE><BODY>"); 
		    sprintf ( buf,"map: '%s'<BR>mapset: '%s'<BR>", Map[i].name, Map[i].mapset);
		    db_append_string (&html, buf);
		}
	    }

	    if ( line >  0) {
		type = Vect_read_line(&Map[i], Points, Cats, line);
		switch ( type ) {
		    case GV_POINT:
			sprintf ( buf, "Point" );
			break;
		    case GV_LINE:
			sprintf ( buf, "Line" );
			break;
		    case GV_BOUNDARY:
			sprintf ( buf, "Boundary" );
			break;
		    case GV_FACE:
			sprintf ( buf, "Face" );
			break;
		    case GV_CENTROID:
			sprintf ( buf, "Centroid" );
			break;
		    default:
			sprintf ( buf, "Unknown" );
		}
		if ( type & GV_LINES ) {
		    if ( G_projection() == 3) l = Vect_line_geodesic_length ( Points );
		    else l = Vect_line_length ( Points );
		}


		if ( topo ) {
		    int n, node[2], nnodes, nnlines, nli, nodeline, left, right;
		    float angle;
		    
		    fprintf(stdout, "-----------------------------------------------\n" );
		    Vect_get_line_areas ( &(Map[i]), line, &left, &right );
		    fprintf(stdout, _("Line: %d  Type: %s  Left: %d  Right: %d  "), line, buf, left, right);
		    if ( type & GV_LINES ) {
			nnodes = 2;
			fprintf(stdout, _("Length: %f\n"), l);
		    } else { /* points */
			nnodes = 1;
			fprintf(stdout, "\n");
		    }
		    
		    Vect_get_line_nodes ( &(Map[i]), line, &node[0], &node[1]);

		    for ( n = 0; n < nnodes; n++ ) { 
			double nx, ny, nz;
			nnlines = Vect_get_node_n_lines (  &(Map[i]), node[n]);

			Vect_get_node_coor (  &(Map[i]), node[n], &nx, &ny, &nz ); 
		        fprintf(stdout, _("  Node[%d]: %d  Number of lines: %d  Coordinates: %.6f, %.6f, %.6f\n"), n, node[n], nnlines, nx, ny, nz );
			
			for ( nli = 0; nli < nnlines; nli++ ) {
			    nodeline =  Vect_get_node_line ( &(Map[i]), node[n], nli );
			    angle =  Vect_get_node_line_angle ( &(Map[i]), node[n], nli );
		            fprintf(stdout, _("    Line: %5d  Angle: %.8f\n"), nodeline, angle);
			}
		    }
		    
		} else if ( txt ) {
		    fprintf(stdout, "%s\n", buf);
		    if ( type & GV_LINES ) fprintf(stdout, _("length %f\n"), l);
		} else { 
		    db_append_string (&html, "feature type: " );
		    db_append_string (&html, buf );
		    db_append_string (&html, "<BR>" );

		    if ( type & GV_LINES ) {  
		        sprintf ( buf, "length: %f<BR>", l );
		        db_append_string (&html, buf );
		    }
		}

		/* Height */
		if ( Vect_is_3d(&(Map[i])) ) {
		    int j;
		    double min, max;

		    if ( type & GV_POINTS ) {
			if ( txt ) fprintf(stdout, _("Point height: %f\n"), Points->z[0]);
			else {
			    sprintf(buf, "Point height: %f<BR>", Points->z[0] );
			    db_append_string (&html, buf);
			}
		    } else if ( type & GV_LINES ) {
			min = max = Points->z[0];
			for ( j = 1; j < Points->n_points; j++) {
			    if ( Points->z[j] < min ) min = Points->z[j];
			    if ( Points->z[j] > max ) max = Points->z[j];
			}
			if ( min == max ) {
			    if ( txt ) fprintf(stdout, _("Line height: %f\n"), min);
			    else {
				sprintf(buf, "Line height: %f<BR>", min);
				db_append_string (&html, buf);
			    }
			} else { 
			    if ( txt ) fprintf(stdout, _("Line height min: %f max: %f\n"), min, max);
			    else {
				sprintf(buf, "Line height min: %f max: %f<BR>", min, max );
				db_append_string (&html, buf);
			    }
			}
		    }
		}

		if(flash){
		   flash_line(&Map[i], line, Points, BLACK);
		   flash_line(&Map[i], line, Points, flash_colr);
		}
	    }

	    if (area > 0) {
		if (Map[i].head.with_z && getz) {
		    if ( txt ) fprintf(stdout, _("Area height: %f\n"), z);
		    else {
			sprintf(buf, "feature type: Area<BR>height: %f<BR>", z );
		        db_append_string (&html, buf);
		    }
		} else {
		    if ( txt ) {
			fprintf(stdout, _("Area\n"));
		    } else {
			sprintf(buf, "feature type: Area<BR>");
		        db_append_string (&html, buf);
		    }
		}

		sq_meters = Vect_get_area_area(&Map[i], area);
		if ( topo ) {
		    int nisles, isleidx, isle, isle_area;
		    
		    nisles = Vect_get_area_num_isles ( &Map[i], area );
		    fprintf(stdout, "-----------------------------------------------\n" );
		    fprintf(stdout, _("Area: %d  Number of isles: %d\n"), area, nisles );

		    for ( isleidx = 0; isleidx < nisles; isleidx++ ) {
		        isle = Vect_get_area_isle ( &Map[i], area, isleidx );
			fprintf(stdout, _("  Isle[%d]: %d\n"), isleidx, isle );
		    }

		    isle = Vect_find_island ( &Map[i], east, north );
		    
		    if ( isle ) {
			isle_area = Vect_get_isle_area ( &Map[i], isle );
		        fprintf(stdout, _("Island: %d  In area: %d\n"), isle, isle_area );
		    }

		} else if ( txt ) {
		    fprintf(stdout, _("Size - Sq Meters: %.3f\t\tHectares: %.3f\n"),
			    sq_meters, (sq_meters / 10000.));

		    fprintf(stdout, _("           Acres: %.3f\t\tSq Miles: %.4f\n"),
			    ((sq_meters * 10.763649) / 43560.),
			    ((sq_meters * 10.763649) / 43560.) / 640.);
		    if (notty) {
			fprintf(stderr,
				_("Size - Sq Meters: %.3f\t\tHectares: %.3f\n"),
				sq_meters, (sq_meters / 10000.));

			fprintf(stderr,
				_("           Acres: %.3f\t\tSq Miles: %.4f\n"),
				((sq_meters * 10.763649) / 43560.),
				((sq_meters * 10.763649) / 43560.) / 640.);
		    }
		    nlines += 3;
		} else {
		    sprintf( buf, "area size: %f<BR>", sq_meters );
                    db_append_string (&html, buf);
		}

		centroid = Vect_get_area_centroid(&Map[i], area);
		if (centroid > 0) {
		    Vect_read_line(&Map[i], Points, Cats, centroid);
		}

		if(flash){
		   flash_area(&Map[i], area, Points, BLACK);
		   flash_area(&Map[i], area, Points, flash_colr);
		}
	    }

	    if ( Cats->n_cats > 0 ) {
		int j;
		for (j = 0; j < Cats->n_cats; j++) {
		    G_debug(2, "field = %d category = %d", Cats->field[j], Cats->cat[j]);
		    if ( txt ) {
			fprintf( stdout, _("Layer: %d\ncategory: %d\n"), Cats->field[j], Cats->cat[j] );
		    } else { 
			db_append_string (&html, "<HR><BR>");
			sprintf(buf, "Layer: %d<BR>category: %d<BR>", Cats->field[j], Cats->cat[j] );
			db_append_string (&html, buf);
		    }
		    Fi = Vect_get_field( &(Map[i]), Cats->field[j]);
		    if (Fi == NULL) {
			if ( txt ) {
			    fprintf( stdout, _("Database connection not defined" ));
			} else { 
			    db_append_string (&html, "Database connection not defined<BR>" );
			}
		    } else {
			int format;
			
			if ( txt ) {
			    fprintf( stdout, _("driver: %s\ndatabase: %s\ntable: %s\nkey column: %s\n"),
				     Fi->driver, Fi->database, Fi->table, Fi->key);
			} else { 
			    sprintf(buf, "driver: %s<BR>database: %s<BR>table: %s<BR>key column: %s<BR>",
				         Fi->driver, Fi->database, Fi->table, Fi->key);
			    db_append_string (&html, buf);
			}
			
			if ( edit && strcmp(Map[i].mapset, G_mapset() ) == 0 ) edit_mode = F_EDIT;
			else edit_mode = F_VIEW;

			if ( txt ) format = F_TXT; else format = F_HTML;
			F_generate ( Fi->driver, Fi->database, Fi->table, Fi->key, Cats->cat[j], 
				 NULL, NULL, edit_mode, format, &form);
			
			if ( txt ) {
			    fprintf( stdout, "%s", form );
			} else { 
			    db_append_string (&html, form); 
			}
			G_free (form);
			G_free(Fi);
		    }
		}
	    }
	    fflush(stdout);
	    if (!txt && !topo ) {
		db_append_string (&html, "</BODY></HTML>"); 
		G_debug ( 3, db_get_string (&html) ); 
		F_open ( title, db_get_string(&html) );
	    }
	 
	    if (flash)
	    {
		flash_colr++;
		if (flash_colr >= G_num_standard_colors())
		    flash_colr = 1;
	    }
	}

	if(flash){
	   R_panel_restore(panell);
	   R_panel_delete(panell);
	}

    } while (!once);
    Vect_destroy_line_struct(Points);

    return 0;
}

/* TODO */
int show_buttons(int once, int flash)
{
    if (once) {
	fprintf(stderr, _("\nClick mouse button on desired location\n\n"));
	nlines = 3;
    }
    else if (nlines >= 18) {	/* display prompt every screen full */
	fprintf(stderr, "\n");
	fprintf(stderr, _("Buttons\n"));
	fprintf(stderr, _(" Left:  what's here\n"));
	if(flash){
	   fprintf (stderr, _(" Middle: toggle flash color\n"));
	   nlines = 5;
	}
	else
	   nlines = 4;	
	fprintf (stderr, _(" Right: quit\n"));
    }

    return 0;
}
